#pragma once
#include <vector>
#include <afxcoll.h>
#include <math.h>
#include "BasicFunction.h"
#include "SOMImage.h"
#include "ImageLab.h"

using namespace std;

struct som_node
{
	int X;
	int Y;
	double* weight;
};

class CSOM
{
public:
	CSOM(int row, int colm, int inputD, int maxC);
	CSOM(int row, int colm, int inputD, int maxC, double* maxInput, double* minInput);
	CSOM(string filepath);

public:
//training of the som
//important:: please normalize data before training
//otherwise, it will lead to too large values, cost overflow of the som
	void		somTraining(double** input, int inputSize);
	void		somTraining(double** input, int inputSize, int* groundtruth);
//get the result of the som
	int*		somResult(void);
//find the winner of the current inputI
	som_node*	findWinner(double* inputI);
	som_node*	findWinner(double* inputI, double& distance);
//calculate the distance betweeen two double pointer
	double		wDistance(double* x, double* y);
//initialize nodes
	void		IniNode(void);
	void		IniNode(double* maxvalue, double* minvalue);
//generate a random weight for som
	double*		ranDouble(void);
	double*		ranDouble(double* maxvalue, double* minvalue);
//update the nodes near winner node
	void		updateNode(som_node* node);
	void		updateNode(som_node* node, rgb nodecolour);
//get the testing result of a testInput
	int			getTestingResults(double* testInput);
	int			getTestingResults(double* testInput, rgb testpattern);
//get the training results
	int*		getTrainingResults();
//normalize the weight of the som
	void		normalizeWeight(void);
//get the output nodes
	double**	getLattice(void);
//get the training winners for other program
	double**	getTrainingWinners(void);
//get the test winners
	double*		getTestWinners(double* testInput);
	double*		getTestWinners(double* testInput, rgb nodecolour);
//destroy the som
	void		somDestroy(void);
//destroy the winners
	void		winnerDestroy(void);
//destroy the training results
	void		trainDestroy(void);
//convert double to string
	CString		douTostr(double dou);
//return the input that is mapped to a node
	int*		GetMapInput(int somsequence, int* mapnum);
//create a view for the SOM
	bool		ViewCreate();
//draw the nodes on the SOM
	void		DrawNodes(int s_x, int s_y, int e_x, int e_y, rgb rgbcolour);
//moving the image to specified positions
	void		MoveImage(int pos);
//write the nodes out
	void		somWriteNodes(CString str);
//set the flag if the som is illustration is created or not, default: created
	void		somIllustration(bool flag);
//load the SOM map from a double matrix
	void		somLoadFromMatrix(double** m_Double, int m_SizeX, int m_SizeY, int m_DataDimension);
//Calculate the topology of the map
	double		somTopology(void);
	int*		somGetLatticePattern(void);
//Save the image to files
	void		somSaveImage(CString m_Destination);
//Get the mean square error
	double		somGetMeanSquareError(void);
//Get the multidimensional scaling metric
	double		somGetMultidimensionalScaling(double** m_input, int m_inpusize);
//Euclidean normalization
	void		somEuclideanNormalization(void);
//Partition the map
	int*		somPartition(void);
//Show the partition of the map
	void		somShowPartition(void);
//Write the output layer to files
	void		somWriteToFile(CString filename);
	void		somSaveWeights(string filename);
//Write the som into an image if the input dim is 3
	void		somWriteToImage(CString filename);
//Plot the som according to their index
	void		somPlotIndex(int* somindex, int indexlength);
//Get the size of the SOM
	void		somGetSize(unsigned int& rows, unsigned int& columns);
//Get the training distance
	vector<double>	somGetTrainingDistance(void);
//Get the size of the map
	_2dpoint GetMapSize();

/*force to change the learning rate and neighborhood function and training cycle of the SOM*/
/*careful to use this, it might cause the SOM to behave stupidly*/
	void		ForceChangeLR(double learningrate);
	void		ForceChangeNF(int neighborhoodsize);
	void		ForceChangeTC(int trainingcycle);

private:

	int			sRow;				//number of rows
	int			sCol;				//number of columns
	int			mapRadius;			//size of the map

	int			inputSize;			//number of inputs
	double**	inputData;			//input data
	int			currentInput;		//current input pointer
	int			inputDim;			//dimension of input
	int*		inputPat;			//training results
	double*		cur_inputData;		//current input data

	som_node**	outLayer;			//output layer of the som
	som_node*	winnerNode;			//winner node
	double**	winners;			//training winners

	double		learningRate;		//learning rate
	double		learningScale;		//learning scale

	int			currentIte;			//current iteration
	int			maxCycle;			//the maximum training cycle
	int			numberIte;			//total number of iteration

	double		neighScale;			//the changing scale for neighhood size
	int			neighSize;			//size of neighors to be updated
	double		neighFunction;		//neighbor function for determining the neighbour of winner node
	double		neighsizedouble;	//neighhood size in double
	double		timeCost;			//total time cost

	bool		train_destroy;		//flag for if training result is destroyed
	CBasicFunction bf;				//a basic function class

	bool		is_view;			//flag for indicating if the SOM's view is created;
	bool		is_illustrate;		//flag indicating view of the SOM during training
	bool		is_loaded;			//flag indicating if the SOM is load without training

	double**	Lattice;			//double pointers to the lattice of the SOM
	int*		LatticePattern;		//pattern of the lattice

	double		iniLearningRate;	//initial learning rate
	double		iniNeighSize;		//initial neighhood size
	double		finalLearningRate;	//final learning rate
	double		finalNeighSize;		//final neighhood size

	double		meanSquareError;	//mean square error of the map

	vector<double> trainingdistance; //distances between input and winners during training

public:
	CSOMImage*	som_image;			//view for illustrating the SOM
private:
	rgb**		lpoha;				//pointer to the som image data;
	int			u_x;				//unit of the nodes x
	int			u_y;				//unit of the nodes y
	int			m_width;
	int			m_height;
	RECT*		node_pos;			//position of nodes on the image;
	LPRECT		lprect;				//create a lprect pointer for update region;

public:
	~CSOM(void);
};
